栈溢出与基本ROP

87次阅读
没有评论

共计 1720 个字符,预计需要花费 5 分钟才能阅读完成。

提醒:本文最后更新于 2024-08-29 09:58,文中所关联的信息可能已发生改变,请知悉!

栈溢出原理

由于 C 语言对数组引用不做任何边界检查从而导致缓冲区溢出(buffer overflow)成为一种很常见的漏洞。根据溢出发生的内存位置通常可以分为栈溢出和堆溢出°其中’由于栈上保存着局部变量和一些状态信息(寄存器值、返回地址等),一旦发生严重的溢出, 攻击者就可以通过覆写返回地址来执行任意代码利用方法包括 shellcode 注入、ret2libc、ROP 等。同时防守方也发展出多种利用缓解机制。

函数调用栈

下面直接举例说明(分为 x86 和 x86-64)

栈溢出与基本 ROP

栈溢出与基本 ROP

x86

栈溢出与基本 ROP

首先被调用函数 func() 的 8 个参数从后问前依次入栈,当执行 call 指令时,下一条指令的地址 0x08048415 作为返回地址入栈。然后程序跳转到 func(),在函数开头,将调用函数的ebp 压栈保存并更新为当前的栈顶地址 esp,作为新的栈基址,而esp 则下移为局部变量开辟空间。函数返回时则相反,通过 leave 指令将 esp 恢复为当前的 ebp,并从栈中将调用者的ebp 弹出,最后 ret 指令弹出返回地址作为 eip,程序回到main() 函数中,最后抬高 esp 清理被调用者的参数,一次函数调用的过程就结束了。

x86-64

栈溢出与基本 ROP

对于 x86-64 的程序,前 6 个参数分别通过 rdirsirdxrcxr8r9进行传递,剩余参数才像 x86 一样从后向前依次压栈。除此之外,我们还发现 func() 没有下移 rsp 开辟栈空间的操作,导致 rbprsp的值是相同的,其实这是一项编译优化:根据 AMD64 ABI 文档的描述 rsp 以下 128 字节的区域被称为 red zone,这是一块被保留的内存, 不会被信号或者中断所修改。于是 func() 作为叶子函数就可以在不调整栈指针的情况下,使用这块内存保存临时数据。

危险函数

第一类危险函数——scanfgets

char buf[10];

scanf("%s", buf);      // 没有限制读取长度,存在栈溢出
scanf("%10s", buf);    // 限制读取长度为 10,但由于 scanf 函数会在字符串末尾自动添加 "\0",如果输入长度正好为 10,就会溢出
scanf("%9s", buf);     // 限制长度为 9,这是安全的

// gets 函数不限制读取长度,很容易构成溢出漏洞

第二类危险函数——strcpystrcatsprintf

int len;
char srcbuf[20];
char destbuf[10];

len = read(0, srcbuf, 19);    // 这里限制了可读入长度,是安全的
src[len] = 0;
strcpy(destbuf, srcbuf);      // 这里将 srcbuf 拷贝到 destbuf,这时候可能造成溢出

可以用来代替上面的的安全函数:strncpystrcatsnprintf,因为这些函数都有一个 size 参数用来限制长度

ret2text

ret2shellcode

ret2shellcode,即控制程序执行 shellcode 代码。shellcode 指的是用于完成某个功能的汇编代码,常见的功能主要是获取目标系统的 shell。一般来说,shellcode 需要我们自己填充。这其实是另外一种典型的利用方法,即此时我们需要自己去填充一些可执行的代码

在栈溢出的基础上,要想执行 shellcode,需要对应的 binary 在运行时,shellcode 所在的区域具有可执行权限。

举例:https://gcc.lv/2021/10/26/20211026_ctf_ret2shellcode/

ret2libc

参考

写在最后

一些东西写得很简略,比如 ret2xxx 部分,虽然是学习笔记而已,大致理解就行,但终究还是因为水平不够才写不出来,希望以后能补充更新

正文完
 0
icvuln
版权声明:本站原创文章,由 icvuln 于2021-11-13发表,共计1720字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)